﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace Odin.Base.IO {
    public static class Input {
        public static bool AllowText = false;
        public static List<Keybinding> Keybindings {
            get {
                return _Keybindings;
            }
        }

        private static List<Keybinding> _Keybindings = new List<Keybinding>();
        private static KeyboardState KState = new KeyboardState();

        public static void Initialize() {
            // Make sure key presses to the window are intercepted
            Interception.KeyPressed += (key, index) => {
                // Set bit
                KState.bits[index] = 1;
            };
            Interception.KeyReleased += (key, index) => {
                // Clear bit
                KState.bits[index] = 0;
            };
            Interception.TextWriting += (key) => {
                // Text is being written
            };

            // LOG: Adding Reserved Keybindings
#if (DEBUG)
            System.Console.WriteLine("LOG: Adding Reserved Keybindings");
#endif

            // Reserved keybindings here
            Odin.Base.IO.Input._Keybindings.Add(new Keybinding(Keys.Escape, 0, new Action[] { null, null, new Action(OdinEngine.Stop) }, true));
            Odin.Base.IO.Input._Keybindings.Add(new Keybinding(Keys.D, 0, new Action[] { new Action(Console.ShowLog), null, null }, true));

            // LOG: Input handler Initialized
#if (DEBUG)
            System.Console.WriteLine("LOG: Input handler Initialized");
#endif
        }

        public static KeyboardState getKeyboardState() {
            return new KeyboardState(KState);
        }

        public static bool AddKeybinding(Keybinding bind) {
            if (_Keybindings.Exists(delegate(Keybinding Lbind) { return (Lbind.Key == bind.Key) && (Lbind.Modifiers == bind.Modifiers); })) {
                return false;
            }
            _Keybindings.Add(bind);
            return true;
        }

        public static bool ModifyKeybinding(Keybinding bind) {
            if (!_Keybindings.Exists(delegate(Keybinding Lbind) { return (Lbind.Key == bind.Key) && (Lbind.Modifiers == bind.Modifiers); })) {
                _Keybindings.Add(bind);
                return false; // Key is not bound - Added
            } else {
                Keybinding toBeChanged = _Keybindings.Find(delegate(Keybinding Lbind) { return (Lbind.Key == bind.Key) && (Lbind.Modifiers == bind.Modifiers); });
                if (toBeChanged.Reserved) {
                    return false; // Key is reserved - Not modified
                }
                toBeChanged.Pressed = bind.Pressed;
                toBeChanged.Held = bind.Held;
                toBeChanged.Released = bind.Released;
            }
            return true;
        }

        public static bool RemoveKeybinding(Keys key, byte mods) {
            if (!_Keybindings.Exists(delegate(Keybinding Lbind) { return (Lbind.Key == key) && (Lbind.Modifiers == mods); })) {
                return false; // Key is not bound - not removed
            } else {
                // Remove all Keybindings that are not Reserved, and match the Key and Modifiers specified
                _Keybindings.RemoveAll(delegate(Keybinding Lbind) { return (Lbind.Key == key) && (Lbind.Modifiers == mods) && !Lbind.Reserved; });
                return true;
            }
        }

        public class Interception : IMessageFilter {
            public static event Action<Keys, int> KeyPressed;
            public static event Action<Keys, int> KeyReleased;
            public static event Action<char> TextWriting;

            public Interception() {
                // LOG: Input Event Interception Started
#if (DEBUG)
                System.Console.WriteLine("LOG: Input Event Interception Started");
#endif
            }

            public bool PreFilterMessage(ref Message m) {
                const int WM_KEYDOWN = 0x0100;
                const int WM_KEYUP = 0x0101;
                const int WCHAR_EVENT = 0x0102;

                if (m.Msg == WM_KEYUP) {
                    Keys key = (Keys)(int)m.WParam & Keys.KeyCode;
                    if (KeyReleased != null) {
                        KeyReleased(key, (int)m.WParam);
                    }
                    IntPtr pointer = Marshal.AllocHGlobal(Marshal.SizeOf(m));
                    Marshal.StructureToPtr(m, pointer, true);
                    TranslateMessage(pointer);
                } else if (m.Msg == WM_KEYDOWN) {
                    Keys key = (Keys)(int)m.WParam & Keys.KeyCode;
                    if (KeyPressed != null) {
                        KeyPressed(key, (int)m.WParam);
                    }
                    IntPtr pointer = Marshal.AllocHGlobal(Marshal.SizeOf(m));
                    Marshal.StructureToPtr(m, pointer, true);
                    TranslateMessage(pointer);
                } else if (m.Msg == WCHAR_EVENT) {
                    char key = (char)m.WParam;

                    if (TextWriting != null && AllowText) {
                        //Debug.WriteLine("Char event: " + key + ", " + m.WParam);
                        TextWriting(key);
                    }
                }

                //Returning false allows the message to continue to the next filter or control.
                return false;
            }

            [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
            public static extern bool TranslateMessage(IntPtr message);
        }
    }
}
